/* generate the fibonacci sequence using coroutines
   Copyright (C) 2018 Ariadne Devos

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <stdio.h>

#include "../lco.h"

static _Noreturn void
produce_fibonacci(struct lco_coroutine *current);

static _Noreturn void
consume_fibonacci(struct lco_coroutine *current);

static struct lco_coroutine test = {};

static struct lco_coroutine fibonacci = {};

static struct lco_position thread;

static unsigned int c;

static unsigned int data[20] = {
	1,
	2,
	3,
	5,
	8,
	13,
	21,
	34,
	55,
	89,
	144,
	233,
	377,
	610,
	987,
	1597,
	2584,
	4181,
	6765,
	10946,
};

void
produce_fibonacci(struct lco_coroutine *current)
{
	unsigned int a = 0, b = 1;
	for (int i = 0; i < 20; i++) {
		c = a + b;
		a = b;
		b = c;
		lco_continue(&test, current, 0);
	}
	lco_pause(current, &thread, 0);
}

void
consume_fibonacci(struct lco_coroutine *current)
{
	unsigned int i = 0;
	while (data[i++] == c) {
		lco_continue(&fibonacci, current, 0);
	}
	_Exit(1);
}

#define STACKSIZE 16384

_Alignas(LCO_STACK_ALIGNMENT)
static char stack0[STACKSIZE];
_Alignas(LCO_STACK_ALIGNMENT)
static char stack1[STACKSIZE];
_Alignas(LCO_STACK_ALIGNMENT)
static char stack2[STACKSIZE];

int
main(int argc, char **argv)
{
	/* XXX write a program to generate this*/
	test.entry = &consume_fibonacci;
	test.stack_size = STACKSIZE;
	test.stack_array = stack0;

	fibonacci.entry = &produce_fibonacci;
	fibonacci.stack_size = STACKSIZE;
	fibonacci.stack_array = stack1;

	lco_auto_sigaltstack(STACKSIZE, stack2);

	lco_init(&test);
	lco_init(&fibonacci);

	lco_resume(&fibonacci, &thread, 0);
	return 0;
}

